/**
 * Storage内容仓库模块
 * @module zrender/Storage
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 * @author errorrik (errorrik@gmail.com)
 * @author pissang (https://github.com/pissang/)
 */


'use strict';
var util = require('./tool/util');
var Group = require('./Group');
var defaultIterateOption = {
    hover: false,
    normal: 'down',
    update: false
};
function shapeCompareFunc(a, b) {
    if (a.zlevel == b.zlevel) {
        if (a.z == b.z) {
            return a.__renderidx - b.__renderidx;
        }
        return a.z - b.z;
    }
    return a.zlevel - b.zlevel;
}
/**
         * 内容仓库 (M)
         * @alias module:zrender/Storage
         * @constructor
         */
var Storage = function () {
    // 所有常规形状，id索引的map
    this._elements = {};
    // 高亮层形状，不稳定，动态增删，数组位置也是z轴方向，靠前显示在下方
    this._hoverElements = [];
    this._roots = [];
    this._shapeList = [];
    this._shapeListOffset = 0;
};
/**
         * 遍历迭代器
         * 
         * @param {Function} fun 迭代回调函数，return true终止迭代
         * @param {Object} [option] 迭代参数，缺省为仅降序遍历普通层图形
         * @param {boolean} [option.hover=true] 是否是高亮层图形
         * @param {string} [option.normal='up'] 是否是普通层图形，迭代时是否指定及z轴顺序
         * @param {boolean} [option.update=false] 是否在迭代前更新形状列表
         * 
         */
Storage.prototype.iterShape = function (fun, option) {
    if (!option) {
        option = defaultIterateOption;
    }
    if (option.hover) {
        // 高亮层数据遍历
        for (var i = 0, l = this._hoverElements.length; i < l; i++) {
            var el = this._hoverElements[i];
            el.updateTransform();
            if (fun(el)) {
                return this;
            }
        }
    }
    if (option.update) {
        this.updateShapeList();
    }
    // 遍历: 'down' | 'up'
    switch (option.normal) {
    case 'down':
        // 降序遍历，高层优先
        var l = this._shapeList.length;
        while (l--) {
            if (fun(this._shapeList[l])) {
                return this;
            }
        }
        break;
    // case 'up':
    default:
        // 升序遍历，底层优先
        for (var i = 0, l = this._shapeList.length; i < l; i++) {
            if (fun(this._shapeList[i])) {
                return this;
            }
        }
        break;
    }
    return this;
};
/**
         * 返回hover层的形状数组
         * @param  {boolean} [update=false] 是否在返回前更新图形的变换
         * @return {Array.<module:zrender/shape/Base>}
         */
Storage.prototype.getHoverShapes = function (update) {
    // hoverConnect
    var hoverElements = [];
    for (var i = 0, l = this._hoverElements.length; i < l; i++) {
        hoverElements.push(this._hoverElements[i]);
        var target = this._hoverElements[i].hoverConnect;
        if (target) {
            var shape;
            target = target instanceof Array ? target : [target];
            for (var j = 0, k = target.length; j < k; j++) {
                shape = target[j].id ? target[j] : this.get(target[j]);
                if (shape) {
                    hoverElements.push(shape);
                }
            }
        }
    }
    hoverElements.sort(shapeCompareFunc);
    if (update) {
        for (var i = 0, l = hoverElements.length; i < l; i++) {
            hoverElements[i].updateTransform();
        }
    }
    return hoverElements;
};
/**
         * 返回所有图形的绘制队列
         * @param  {boolean} [update=false] 是否在返回前更新该数组
         * 详见{@link module:zrender/shape/Base.prototype.updateShapeList}
         * @return {Array.<module:zrender/shape/Base>}
         */
Storage.prototype.getShapeList = function (update) {
    if (update) {
        this.updateShapeList();
    }
    return this._shapeList;
};
/**
         * 更新图形的绘制队列。
         * 每次绘制前都会调用，该方法会先深度优先遍历整个树，更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中，
         * 最后根据绘制的优先级（zlevel > z > 插入顺序）排序得到绘制队列
         */
Storage.prototype.updateShapeList = function () {
    this._shapeListOffset = 0;
    for (var i = 0, len = this._roots.length; i < len; i++) {
        var root = this._roots[i];
        this._updateAndAddShape(root);
    }
    this._shapeList.length = this._shapeListOffset;
    for (var i = 0, len = this._shapeList.length; i < len; i++) {
        this._shapeList[i].__renderidx = i;
    }
    this._shapeList.sort(shapeCompareFunc);
};
Storage.prototype._updateAndAddShape = function (el, clipShapes) {
    if (el.ignore) {
        return;
    }
    el.updateTransform();
    if (el.clipShape) {
        // clipShape 的变换是基于 group 的变换
        el.clipShape.parent = el;
        el.clipShape.updateTransform();
        // PENDING 效率影响
        if (clipShapes) {
            clipShapes = clipShapes.slice();
            clipShapes.push(el.clipShape);
        } else {
            clipShapes = [el.clipShape];
        }
    }
    if (el.type == 'group') {
        for (var i = 0; i < el._children.length; i++) {
            var child = el._children[i];
            // Force to mark as dirty if group is dirty
            child.__dirty = el.__dirty || child.__dirty;
            this._updateAndAddShape(child, clipShapes);
        }
        // Mark group clean here
        el.__dirty = false;
    } else {
        el.__clipShapes = clipShapes;
        this._shapeList[this._shapeListOffset++] = el;
    }
};
/**
         * 修改图形(Shape)或者组(Group)
         * 
         * @param {string|module:zrender/shape/Base|module:zrender/Group} el
         * @param {Object} [params] 参数
         */
Storage.prototype.mod = function (el, params) {
    if (typeof el === 'string') {
        el = this._elements[el];
    }
    if (el) {
        el.modSelf();
        if (params) {
            // 如果第二个参数直接使用 shape
            // parent, _storage, __clipShapes 三个属性会有循环引用
            // 主要为了向 1.x 版本兼容，2.x 版本不建议使用第二个参数
            if (params.parent || params._storage || params.__clipShapes) {
                var target = {};
                for (var name in params) {
                    if (name === 'parent' || name === '_storage' || name === '__clipShapes') {
                        continue;
                    }
                    if (params.hasOwnProperty(name)) {
                        target[name] = params[name];
                    }
                }
                util.merge(el, target, true);
            } else {
                util.merge(el, params, true);
            }
        }
    }
    return this;
};
/**
         * 移动指定的图形(Shape)或者组(Group)的位置
         * @param {string} shapeId 形状唯一标识
         * @param {number} dx
         * @param {number} dy
         */
Storage.prototype.drift = function (shapeId, dx, dy) {
    var shape = this._elements[shapeId];
    if (shape) {
        shape.needTransform = true;
        if (shape.draggable === 'horizontal') {
            dy = 0;
        } else if (shape.draggable === 'vertical') {
            dx = 0;
        }
        if (!shape.ondrift || shape.ondrift && !shape.ondrift(dx, dy)) {
            shape.drift(dx, dy);
        }
    }
    return this;
};
/**
         * 添加高亮层数据
         * 
         * @param {module:zrender/shape/Base} shape
         */
Storage.prototype.addHover = function (shape) {
    shape.updateNeedTransform();
    this._hoverElements.push(shape);
    return this;
};
/**
         * 清空高亮层数据
         */
Storage.prototype.delHover = function () {
    this._hoverElements = [];
    return this;
};
/**
         * 是否有图形在高亮层里
         * @return {boolean}
         */
Storage.prototype.hasHoverShape = function () {
    return this._hoverElements.length > 0;
};
/**
         * 添加图形(Shape)或者组(Group)到根节点
         * @param {module:zrender/shape/Shape|module:zrender/Group} el
         */
Storage.prototype.addRoot = function (el) {
    // Element has been added
    if (this._elements[el.id]) {
        return;
    }
    if (el instanceof Group) {
        el.addChildrenToStorage(this);
    }
    this.addToMap(el);
    this._roots.push(el);
};
/**
         * 删除指定的图形(Shape)或者组(Group)
         * @param {string|Array.<string>} [elId] 如果为空清空整个Storage
         */
Storage.prototype.delRoot = function (elId) {
    if (typeof elId == 'undefined') {
        // 不指定elId清空
        for (var i = 0; i < this._roots.length; i++) {
            var root = this._roots[i];
            if (root instanceof Group) {
                root.delChildrenFromStorage(this);
            }
        }
        this._elements = {};
        this._hoverElements = [];
        this._roots = [];
        this._shapeList = [];
        this._shapeListOffset = 0;
        return;
    }
    if (elId instanceof Array) {
        for (var i = 0, l = elId.length; i < l; i++) {
            this.delRoot(elId[i]);
        }
        return;
    }
    var el;
    if (typeof elId == 'string') {
        el = this._elements[elId];
    } else {
        el = elId;
    }
    var idx = util.indexOf(this._roots, el);
    if (idx >= 0) {
        this.delFromMap(el.id);
        this._roots.splice(idx, 1);
        if (el instanceof Group) {
            el.delChildrenFromStorage(this);
        }
    }
};
Storage.prototype.addToMap = function (el) {
    if (el instanceof Group) {
        el._storage = this;
    }
    el.modSelf();
    this._elements[el.id] = el;
    return this;
};
Storage.prototype.get = function (elId) {
    return this._elements[elId];
};
Storage.prototype.delFromMap = function (elId) {
    var el = this._elements[elId];
    if (el) {
        delete this._elements[elId];
        if (el instanceof Group) {
            el._storage = null;
        }
    }
    return this;
};
/**
         * 清空并且释放Storage
         */
Storage.prototype.dispose = function () {
    this._elements = this._renderList = this._roots = this._hoverElements = null;
};
module.exports = Storage || module.exports;;